package com.geo.content.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.geo.content.entity.MediaFile;
import com.geo.content.mapper.MediaFileMapper;
import com.geo.content.storage.StorageService;
import com.geo.common.exception.BusinessException;
import com.geo.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;

/**
 * 文件上传服务
 * 
 * @author GEO Team
 * @version 1.0
 */
@Slf4j
@Service
public class FileUploadService {
    
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FileUploadService.class);
    
    @Autowired
    private StorageService storageService;
    
    @Autowired
    private MediaFileMapper mediaFileMapper;
    
    @Autowired
    private MediaProcessingService mediaProcessingService;
    
    // 允许的文件类型
    private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList(
            "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/bmp"
    );
    
    private static final List<String> ALLOWED_VIDEO_TYPES = Arrays.asList(
            "video/mp4", "video/avi", "video/mov", "video/wmv", "video/flv", "video/webm"
    );
    
    private static final List<String> ALLOWED_AUDIO_TYPES = Arrays.asList(
            "audio/mp3", "audio/wav", "audio/aac", "audio/ogg", "audio/m4a"
    );
    
    private static final List<String> ALLOWED_DOCUMENT_TYPES = Arrays.asList(
            "application/pdf", "application/msword", 
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "application/vnd.ms-powerpoint",
            "application/vnd.openxmlformats-officedocument.presentationml.presentation"
    );
    
    // 文件大小限制（字节）
    private static final long MAX_IMAGE_SIZE = 10 * 1024 * 1024; // 10MB
    private static final long MAX_VIDEO_SIZE = 500 * 1024 * 1024; // 500MB
    private static final long MAX_AUDIO_SIZE = 50 * 1024 * 1024; // 50MB
    private static final long MAX_DOCUMENT_SIZE = 20 * 1024 * 1024; // 20MB
    
    /**
     * 上传单个文件
     * @param file 文件
     * @param merchantId 商家ID
     * @param contentId 内容ID（可选）
     * @param description 文件描述
     * @return 媒体文件信息
     */
    @Transactional
    public MediaFile uploadFile(MultipartFile file, String merchantId, String contentId, String description) {
        // 基础验证
        validateFile(file);
        
        try {
            // 生成文件信息
            String originalName = file.getOriginalFilename();
            String fileExtension = FileUtil.extName(originalName);
            String fileName = generateFileName(fileExtension);
            String filePath = generateFilePath(fileName);
            
            // 计算文件哈希
            byte[] fileBytes = file.getBytes();
            String fileHash = SecureUtil.md5().digestHex(fileBytes);
            
            // 检查文件是否已存在
            MediaFile existingFile = mediaFileMapper.selectByHash(fileHash);
            if (existingFile != null && existingFile.getMerchantId().equals(merchantId)) {
                log.info("文件已存在，复用已有文件: {}", existingFile.getId());
                return existingFile;
            }
            
            // 确定文件类型
            MediaFile.FileType fileType = MediaFile.FileType.fromMimeType(file.getContentType());
            
            // 上传到存储服务
            String fileUrl = storageService.uploadFile(file.getInputStream(), filePath, file.getContentType());
            
            // 创建媒体文件记录
            MediaFile mediaFile = MediaFile.builder()
                    .contentId(contentId)
                    .merchantId(merchantId)
                    .fileName(fileName)
                    .originalName(originalName)
                    .filePath(filePath)
                    .fileUrl(fileUrl)
                    .fileType(fileType)
                    .mimeType(file.getContentType())
                    .fileSize(file.getSize())
                    .fileHash(fileHash)
                    .status(MediaFile.FileStatus.UPLOADED)
                    .description(description)
                    .storageLocation(storageService.getStorageLocation())
                    .bucketName(storageService.getBucketName())
                    .processStatus(MediaFile.ProcessStatus.PENDING)
                    .isPublic(false)
                    .downloadCount(0L)
                    .accessCount(0L)
                    .build();
            
            mediaFileMapper.insert(mediaFile);
            
            // 异步处理媒体文件
            mediaProcessingService.processMediaFileAsync(mediaFile);
            
            log.info("文件上传成功: {} -> {}", originalName, mediaFile.getId());
            
            return mediaFile;
            
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new BusinessException(ResultCode.FILE_UPLOAD_FAILED, "文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * 批量上传文件
     * @param files 文件列表
     * @param merchantId 商家ID
     * @param contentId 内容ID（可选）
     * @return 媒体文件列表
     */
    @Transactional
    public List<MediaFile> uploadFiles(List<MultipartFile> files, String merchantId, String contentId) {
        if (files == null || files.isEmpty()) {
            throw new BusinessException(ResultCode.INVALID_PARAM, "文件列表不能为空");
        }
        
        if (files.size() > 10) {
            throw new BusinessException(ResultCode.INVALID_PARAM, "批量上传最多支持10个文件");
        }
        
        return files.stream()
                .map(file -> uploadFile(file, merchantId, contentId, null))
                .toList();
    }
    
    /**
     * 删除文件
     * @param fileId 文件ID
     * @param merchantId 商家ID
     */
    @Transactional
    public void deleteFile(String fileId, String merchantId) {
        MediaFile mediaFile = mediaFileMapper.selectById(fileId);
        
        if (mediaFile == null) {
            throw new BusinessException(ResultCode.FILE_NOT_FOUND, "文件不存在");
        }
        
        if (!mediaFile.getMerchantId().equals(merchantId)) {
            throw new BusinessException(ResultCode.ACCESS_DENIED, "无权限删除此文件");
        }
        
        try {
            // 从存储服务删除文件
            storageService.deleteFile(mediaFile.getFilePath());
            
            // 删除缩略图
            if (StrUtil.isNotBlank(mediaFile.getThumbnailPath())) {
                storageService.deleteFile(mediaFile.getThumbnailPath());
            }
            
            // 逻辑删除数据库记录
            mediaFileMapper.deleteById(fileId);
            
            log.info("文件删除成功: {}", fileId);
            
        } catch (Exception e) {
            log.error("文件删除失败", e);
            throw new BusinessException(ResultCode.FILE_DELETE_FAILED, "文件删除失败: " + e.getMessage());
        }
    }
    
    /**
     * 获取文件信息
     * @param fileId 文件ID
     * @param merchantId 商家ID
     * @return 媒体文件信息
     */
    public MediaFile getFileInfo(String fileId, String merchantId) {
        MediaFile mediaFile = mediaFileMapper.selectById(fileId);
        
        if (mediaFile == null) {
            throw new BusinessException(ResultCode.FILE_NOT_FOUND, "文件不存在");
        }
        
        if (!mediaFile.getMerchantId().equals(merchantId) && !mediaFile.getIsPublic()) {
            throw new BusinessException(ResultCode.ACCESS_DENIED, "无权限访问此文件");
        }
        
        // 增加访问次数
        mediaFile.setAccessCount(mediaFile.getAccessCount() + 1);
        mediaFile.setLastAccessedAt(LocalDateTime.now());
        mediaFileMapper.updateById(mediaFile);
        
        return mediaFile;
    }
    
    /**
     * 验证文件
     * @param file 文件
     */
    private void validateFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ResultCode.INVALID_PARAM, "文件不能为空");
        }
        
        String mimeType = file.getContentType();
        if (StrUtil.isBlank(mimeType)) {
            throw new BusinessException(ResultCode.INVALID_FILE_TYPE, "无法识别文件类型");
        }
        
        // 检查文件类型是否被允许
        boolean isAllowed = ALLOWED_IMAGE_TYPES.contains(mimeType) ||
                           ALLOWED_VIDEO_TYPES.contains(mimeType) ||
                           ALLOWED_AUDIO_TYPES.contains(mimeType) ||
                           ALLOWED_DOCUMENT_TYPES.contains(mimeType);
        
        if (!isAllowed) {
            throw new BusinessException(ResultCode.INVALID_FILE_TYPE, "不支持的文件类型: " + mimeType);
        }
        
        // 检查文件大小
        long fileSize = file.getSize();
        long maxSize = getMaxFileSizeByType(mimeType);
        
        if (fileSize > maxSize) {
            throw new BusinessException(ResultCode.FILE_TOO_LARGE, 
                    String.format("文件大小超过限制，当前: %s，最大: %s", 
                            formatFileSize(fileSize), formatFileSize(maxSize)));
        }
    }
    
    /**
     * 根据文件类型获取最大文件大小
     * @param mimeType MIME类型
     * @return 最大文件大小
     */
    private long getMaxFileSizeByType(String mimeType) {
        if (ALLOWED_IMAGE_TYPES.contains(mimeType)) {
            return MAX_IMAGE_SIZE;
        } else if (ALLOWED_VIDEO_TYPES.contains(mimeType)) {
            return MAX_VIDEO_SIZE;
        } else if (ALLOWED_AUDIO_TYPES.contains(mimeType)) {
            return MAX_AUDIO_SIZE;
        } else if (ALLOWED_DOCUMENT_TYPES.contains(mimeType)) {
            return MAX_DOCUMENT_SIZE;
        }
        return MAX_DOCUMENT_SIZE; // 默认
    }
    
    /**
     * 生成文件名
     * @param extension 文件扩展名
     * @return 文件名
     */
    private String generateFileName(String extension) {
        return IdUtil.fastSimpleUUID() + "." + extension;
    }
    
    /**
     * 生成文件路径
     * @param fileName 文件名
     * @return 文件路径
     */
    private String generateFilePath(String fileName) {
        String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        return "uploads/" + datePath + "/" + fileName;
    }
    
    /**
     * 格式化文件大小
     * @param size 文件大小（字节）
     * @return 格式化后的大小
     */
    private String formatFileSize(long size) {
        String[] units = {"B", "KB", "MB", "GB"};
        int unitIndex = 0;
        double fileSize = size;
        
        while (fileSize >= 1024 && unitIndex < units.length - 1) {
            fileSize /= 1024;
            unitIndex++;
        }
        
        return String.format("%.1f %s", fileSize, units[unitIndex]);
    }
}